package net.zschech.gwt.chat.client;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.zschech.gwt.chat.client.StatusUpdate.Status;
import net.zschech.gwt.chat.client.domain.Color;
import net.zschech.gwt.chat.client.domain.Coordinates;
import net.zschech.gwt.chat.client.domain.GameMove;
import net.zschech.gwt.comet.client.CometClient;
import net.zschech.gwt.comet.client.CometListener;
import net.zschech.gwt.comet.client.CometSerializer;
import net.zschech.gwt.comet.client.SerialTypes;

import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;

import com.google.gwt.canvas.dom.client.CssColor;
import com.google.gwt.canvas.dom.client.FillStrokeStyle;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;

import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;

public class ChatEntryPoint implements EntryPoint {
  
  Logger logger = Logger.getLogger("theLogger");

  private ChatServiceAsync chatService;
  private CometClient cometClient;

  private String username;

  private HTML messages;
  private ScrollPanel scrollPanel;
  private VerticalPanel vp = new VerticalPanel();

  Canvas canvas;
  Canvas chessBoard;
  Button clearTest = new Button("clear test");

  /**
   *  canvas size, in px
   *  Must note that height and width must divisible by this equation 
   * (width-2*margin) / 18;
   */
  static final int margin = 25;
  static final int lineWidth = 4;
  
  static final int BIGSIZE = 40*18+2*margin;
  static final int SMALLSIZE = 25*18+2*margin;
  static final int height = SMALLSIZE;
  static final int width = SMALLSIZE;
  Map<String, GameMove> boardCooradinates =new HashMap<String, GameMove>();
  Context2d context;
  List<String> users = new ArrayList<String>();
  int userFlag = 0;
  final FillStrokeStyle fillStyle = CssColor.make("rgba(255,255,255,1)");
  
  static final int  unitWidth = (width-2*margin) / 18;

  // mouse positions relative to canvas
  int mouseX, mouseY;

  @SerialTypes({ChatMessage.class, StatusUpdate.class,GameMessage.class})
  public static abstract class ChatCometSerializer extends CometSerializer {
  }

  @Override
  public void onModuleLoad() {
 
    final Context2d boardContext;
    
    final FillStrokeStyle strokeStyle = CssColor.make("#333300");
 

    canvas = Canvas.createIfSupported();
    // this canvas is used to draw the chessboard
    Canvas chessBoard = Canvas.createIfSupported();
    canvas.setWidth(width + "px");
    canvas.setHeight(height + "px");
    canvas.setCoordinateSpaceWidth(width);
    canvas.setCoordinateSpaceHeight(height);
    chessBoard.setCoordinateSpaceWidth(width);
    chessBoard.setCoordinateSpaceHeight(height);
    boardContext = chessBoard.getContext2d();
    boardContext.setStrokeStyle(strokeStyle);
    boardContext.setLineWidth(lineWidth);

  
    drawBoard(boardContext);
    

    chatService = GWT.create(ChatService.class);
    chatService.getUsername(new AsyncCallback<String>() {
      @Override
      public void onSuccess(String username) {
        if (username == null) {
          showLogonDialog();
        } else {
          loggedOn(username);
        }
      }

      @Override
      public void onFailure(Throwable caught) {
        output(caught.toString(), "red");
        // assume they are not logged in
        showLogonDialog();
      }
    });

    FlowPanel controls = new FlowPanel();
    final ListBox status = new ListBox();
    for (Status s : Status.values()) {
      status.addItem(s.name());
    }
    status.addChangeHandler(new ChangeHandler() {
      @Override
      public void onChange(ChangeEvent event) {
        setStatus(Status.values()[status.getSelectedIndex()]);
      }
    });

    final TextBox input = new TextBox();
    Button send = new Button("Send", new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        sendMessage(input.getValue());
      }
    });
    Button logout = new Button("Logout", new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        logout();
      }
    });

    controls.add(status);
    controls.add(input);
    controls.add(send);
    controls.add(logout);
    controls.add(clearTest);
    vp.add(canvas);
    vp.setStyleName("verticalback");

    context = canvas.getContext2d();
    
    clearTest.addClickHandler(new ClickHandler() {
    
      @Override
      public void onClick(ClickEvent event) {
        context.clearRect(169, 167, 40, 40);
        	
        drawBoard(boardContext);
     
      }
    });

    canvas.addClickHandler(new ClickHandler() {

      @Override
      public void onClick(ClickEvent event) {
        // TODO Auto-generated method stub
        mouseX = event.getRelativeX(canvas.getElement());
        mouseY = event.getRelativeY(canvas.getElement());
        GameMove gm = new GameMove();
        gm = getMove(mouseX,mouseY,Color.black,context);
        chatService.sendGameMove(gm, new AsyncCallback<Void>(){

			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub
				  output(caught.toString(), "red");
			}	

			@Override
			public void onSuccess(Void result) {
				// TODO Auto-generated method stub
				
			}});
     
    
       
   /*     Image img = new Image("like.png");
        ImageElement imageElement = (ImageElement) img.getElement().cast();
        context.drawImage(imageElement, mouseX, mouseY);*/
        sendMessage("I am moving to " + mouseX + "/" + mouseY);

      }
    });

    DockPanel dockPanel = new DockPanel();
    messages = new HTML();
    scrollPanel = new ScrollPanel();
    scrollPanel.setHeight("250px");
    scrollPanel.add(messages);
    dockPanel.add(vp, DockPanel.NORTH);
    dockPanel.add(scrollPanel, DockPanel.CENTER);
    dockPanel.add(controls, DockPanel.SOUTH);
    context.getCanvas().focus();
    context.drawImage(boardContext.getCanvas(), 0, 0);
    RootPanel.get().add(dockPanel);
   // RootPanel.get().add(chessBoard);
  }



  private void showLogonDialog() {
    final DialogBox dialogBox = new DialogBox();
    dialogBox.setText("Login");
    dialogBox.setGlassEnabled(true);
    dialogBox.setAnimationEnabled(true);

    VerticalPanel verticalPanel = new VerticalPanel();
    verticalPanel.setSpacing(4);
    dialogBox.setWidget(verticalPanel);

    final TextBox username = new TextBox();
    verticalPanel.add(new HTML("Username:"));
    verticalPanel.add(username);

    Button closeButton = new Button("Logon", new ClickHandler() {
      public void onClick(ClickEvent event) {
        dialogBox.hide();
        login(username.getValue());
      }
    });
    verticalPanel.add(closeButton);

    dialogBox.center();
    dialogBox.show();
  }

  private void login(final String username) {
    chatService.login(username, new AsyncCallback<Void>() {

      @Override
      public void onSuccess(Void result) {
        loggedOn(username);
      }

      @Override
      public void onFailure(Throwable caught) {
        output(caught.toString(), "red");
      }
    });
  }

  private void logout() {
    chatService.logout(username, new AsyncCallback<Void>() {

      @Override
      public void onSuccess(Void result) {
        cometClient.stop();
        showLogonDialog();
      }

      @Override
      public void onFailure(Throwable caught) {
        output(caught.toString(), "red");
      }
    });
  }

  private void loggedOn(String username) {
    this.username = username;
   
    userFlag++;
    output("logged in as " + username, "silver");
    CometSerializer serializer = GWT.create(ChatCometSerializer.class);
    cometClient =
        new CometClient(GWT.getModuleBaseURL() + "comet", serializer, new CometListener() {
          public void onConnected(int heartbeat) {
            output("connected " + heartbeat, "silver");
          }

          public void onDisconnected() {
            output("disconnected", "silver");
          }

          public void onError(Throwable exception, boolean connected) {
            output("error " + connected + " " + exception, "red");
          }

          public void onHeartbeat() {
            output("heartbeat", "silver");
          }

          public void onRefresh() {
            output("refresh", "silver");
          }

          public void onMessage(List<? extends Serializable> messages) {
            for (Serializable message : messages) {
              if (message instanceof ChatMessage) {
                ChatMessage chatMessage = (ChatMessage) message;
                output(chatMessage.getUsername() + ": " + chatMessage.getMessage(), "black");
              } else if (message instanceof StatusUpdate) {
                StatusUpdate statusUpdate = (StatusUpdate) message;
                output(statusUpdate.getUsername() + ": " + statusUpdate.getStatus(), "green");
              }else if(message instanceof GameMove){//Game Message Initiate
            	 GameMove  gm = (GameMove)message;
            	 if(userFlag==1){
            		 gm.setColor(Color.black);
            	 }else{
            		 gm.setColor(Color.white);
            	 }
            	  context.beginPath();
                  context.arc(gm.getMoveX(), gm.getMoveY(), 10, 0, 360);
                  if(gm.getColor()==Color.black){
                	 
                  }else{
                	  context.setFillStyle(fillStyle);
                  }
                  context.fill();        
                 
                /*  context.stroke();
                  context.closePath();*/
              }
              else {
                output("unrecognised message " + message, "red");
              }
            }
          }
        });
    cometClient.start();
  }

  private void sendMessage(String message) {
    chatService.send(message, new AsyncCallback<Void>() {
      @Override
      public void onSuccess(Void result) {
      }

      @Override
      public void onFailure(Throwable caught) {
        output(caught.toString(), "red");
      }
    });
  }

  private void setStatus(Status status) {
    chatService.setStatus(status, new AsyncCallback<Void>() {
      @Override
      public void onSuccess(Void result) {
      }

      @Override
      public void onFailure(Throwable caught) {
        output(caught.toString(), "red");
      }
    });
  }

  public void output(String text, String color) {
    DivElement div = Document.get().createDivElement();
    div.setInnerText(text);
    div.setAttribute("style", "color:" + color);
    messages.getElement().appendChild(div);
    scrollPanel.scrollToBottom();
  }
  
  /**
   * get the gamemove
   * 1.calculate the approximate  location of the chess and make it accurate to the chessboard 
   * 2.record to the chessboard layout recorder data structure
   * @param x
   * @param y
   * @return
   */
  private GameMove getMove(int x ,int y,Color color,Context2d context){
	  GameMove gm = new GameMove();
	  int analogX,analogY;
	  int factor; //the times that length to the unit width

	  gm.setColor(color);
	  
	  //set analog x
	  if((x-margin)%unitWidth==0){
		  gm.setMoveX(x);		
	  }else{
		  if((x-margin)%unitWidth<unitWidth/2){
			  factor =  (x-margin)/unitWidth;			
		  }else{
			  factor =  (x-margin)/unitWidth+1;			
		  }
		  analogX =margin +factor*unitWidth; 
		  logger.log(Level.FINE,"x="+ x +"  analogX= "+analogX+"factor ="+ factor);
		  gm.setMoveX(analogX);
	  }
	  
	  //set analog y
	  if((y-margin)%unitWidth==0){
		  gm.setMoveY(y);
	  }else{
		  if((y-margin)%unitWidth<unitWidth/2){
			  factor =  (y-margin)/unitWidth;			
		  }else{
			  factor =  (y-margin)/unitWidth+1;			
		  }
		  analogY =margin +factor*unitWidth; 		
		  gm.setMoveY(analogY);
	  }
	  boardCooradinates.put(String.valueOf(gm.getMoveX()+gm.getMoveY()), gm);
	  return gm;
  }
  
  private void drawBoard(final Context2d boardContext) {
		for (int i = 0; i < 19; i++) {
	      // draw 19*19 weiqi chessboard      
	      boardContext.moveTo(margin, margin + i * unitWidth);      
	      boardContext.lineTo( width-margin,i * unitWidth+margin);
	      logger.log(Level.FINE , "length is "+ (width-margin-margin  ));
	      boardContext.stroke();
	    }
	    for (int i = 0; i < 19; i++) {
	      // draw 19*19 weiqi chessboard      
	      boardContext.moveTo(margin + i * unitWidth,margin );      
	      boardContext.lineTo( i * unitWidth+margin,width-margin);     
	      logger.log(Level.FINE , "now coordinate: 0,"+i * unitWidth+",line to:"+i * unitWidth+",600");
	      boardContext.stroke();
	    }
	}
  
}
